package com.cmii.sjw.department.control.request;

import cn.hutool.crypto.digest.HMac;
import cn.hutool.crypto.digest.HmacAlgorithm;
import cn.hutool.http.HttpException;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpStatus;
import com.rw.tool.util.cache.CacheHashMap;
import com.rw.tool.util.json.JsonUtil;
import com.rw.tool.util.string.StringFormatter;
import com.rw.tool.util.string.StringUtil;
import lombok.extern.slf4j.Slf4j;

import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * IRS调用工具类
 */
@Slf4j
public class IRSUtil {
    /**
     * 获取token的URL
     * 使用这个URL的时候记得需要配置DNS
     * nameserver 23.210.52.8
     * nameserver 23.210.52.9
     * 
     * 或者配置HOST（不推荐）
     * 23.210.52.237 drs.dsjfzj.cq.cegn
     */
    private static final String TOKEN_URL = "https://drs.dsjfzj.cq.cegn:11007/apisix/token";

    /**
     * token缓存器
     */
    private static final CacheHashMap<String, String> TOKEN_CACHE = new CacheHashMap<>();

    private static final String TOKEN_KEY = "token";

    /**
     * TOKEN调用的请求头
     */
    public static final String TOKEN_HEADER = "X-ACCESS-TOKEN";

    /**
     * 获取token
     */
    public static String getToken(String accessKey, String secretKey) {
        // 如果缓存里面已经有一个token了，则使用这个token
        String key = accessKey + ":" + secretKey;
        String token = TOKEN_CACHE.get(key);
        if (!StringUtil.isBlank(token)) {
            return token;
        }

        // 获取token
        TokenResult newToken = getTokenWithoutCache(accessKey, secretKey);

        // 根据token的过期时间设置缓存
        long survivalTime = newToken.getExpireTime() * 1000 - System.currentTimeMillis();
        TOKEN_CACHE.put(key, newToken.getToken(), survivalTime);

        return newToken.getToken();
    }

    /**
     * 获取token（不使用缓存）
     */
    public static TokenResult getTokenWithoutCache(String accessKey, String secretKey) {
        // 构造参数
        Map<String, Object> param = new HashMap<>();
        param.put("access_key", accessKey);

        // 获取时间戳，注意以秒为单位
        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
        param.put("timestamp", timestamp);

        // 计算签名
        String sign = getSignature(param, secretKey);
        param.put("signature", sign);

        // 打印日志
        String logMessage = "send token request : accessKey:{}, " +
                "secretKey:{}, timestamp:{}, signature:{}";
        log.info(logMessage, accessKey, secretKey, timestamp, sign);

        // 发送请求
        HttpResponse execute = HttpRequest.get(TOKEN_URL)
                .form(param)
                .execute();

        // 如果没有成功
        String body = execute.body();
        if (execute.getStatus() != HttpStatus.HTTP_OK) {
            // 输出日志
            String errorMsg = "request token failed!" +
                    "httpStatus:{}, accessKey:{}, secretKey:{}, timestamp:{}, " +
                    "signature:{}, responseBody:{}";
            log.error(errorMsg, execute.getStatus(), accessKey, secretKey, timestamp, sign, body);
            String exceptionMsg = "发起token请求失败！请联系技术人员，错误信息详见日志";
            throw new HttpException(exceptionMsg);
        }

        // 构造返回信息
        TokenResult tokenResult = JsonUtil.toObject(body, TokenResult.class);

        // 如果返回值当中有错误信息，则抛出异常
        if (!tokenResult.success()) {
            // 输出日志
            String errorMsgTemplate = "parse token result failed!" +
                    "accessKey:{}, secretKey:{}, timestamp:{}, signature:{}, responseBody:{}";
            log.error(errorMsgTemplate, accessKey, secretKey, timestamp, sign, body);
            String exceptionMsg = "解析token返回值失败！请联系技术人员，错误信息详见日志";
            throw new HttpException(exceptionMsg);
        }

        // 成功返回结果
        log.info("get token success! token:{}", JsonUtil.toJson(tokenResult));
        return tokenResult;
    }

    /**
     * 获取签名
     *
     * @param param 调用的参数
     * @return 签名结果
     */
    public static String getSignature(Map<String, Object> param, String secretKey) {
        String signStr = param.entrySet().stream()
                .map(IRSUtil::entryToString)
                .sorted()
                .collect(Collectors.joining("&"));
        // 加密
        HMac hmac = new HMac(HmacAlgorithm.HmacSHA256, secretKey.getBytes());
        return hmac.digestHex(signStr);
    }

    private static String entryToString(Map.Entry<String, Object> entry) {
        String template = "{}={}";
        return StringFormatter.format(template, entry.getKey(), entry.getValue());
    }

    /**
     * 测试用计算token
     */
    public static void testTokenRequest(String accessKey, String secretKey, String timestamp) {
        // 构造参数
        Map<String, Object> param = new HashMap<>();
        param.put("access_key", accessKey);

        // 获取时间戳
        param.put("timestamp", String.valueOf(timestamp));

        // 计算签名
        String sign = getSignature(param, secretKey);
        param.put("signature", String.valueOf(sign));
        System.out.println(TOKEN_URL);
        for (Map.Entry<String, Object> entry : param.entrySet()) {
            System.out.println(entry.getKey() + ":" + entry.getValue());
        }
    }

    /**
     * 测试用计算token
     */
    public static void testTokenRequest(String accessKey, String secretKey) {
        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
        testTokenRequest(accessKey, secretKey, timestamp);
    }
}